#!/usr/bin/env python
""" AnyBackup is a windows specific backup program to keep multiple volumes in sync with multiple volumes
    Copyright (C) 2011  Andrew Hopkinson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Exported Classes:

    listdialog -- a custom dialog which allows a user to provide regular expressions for file extensions to include 
                  filenames to exclude.
    """
import re
import os

import wx
import wx.lib.agw.aui as aui

import DriveUtil
from enum import AEnums
from sql import SQLLite


class remoteDialog(wx.Dialog):
    def __init__(self, parent, driveRef):
        self.r = SQLLite()
        self._drive = driveRef
        self._host = self._drive.get(AEnums.REMOTEHOST, None)
        self._path = self._drive.get(AEnums.REMOTEPATH, None)
        label = '%s - Remote Index' % self._drive[AEnums.NAME]
        wx.Dialog.__init__(self, parent, -1, label, wx.DefaultPosition, wx.Size(200, 135))
        hostStr = self._drive.get(AEnums.REMOTEHOST, '')
        pathStr = self._drive.get(AEnums.REMOTEPATH, '')
        hostPanel = wx.Panel(self, -1)
        pathPanel = wx.Panel(self, -1)
        buttonPanel = wx.Panel(self, -1)
        mainBox = wx.BoxSizer(wx.VERTICAL)
        hostBox = wx.BoxSizer(wx.HORIZONTAL)
        pathBox = wx.BoxSizer(wx.HORIZONTAL)
        buttonBox = wx.BoxSizer(wx.HORIZONTAL)
        self.hostCtrl = wx.TextCtrl(hostPanel, -1, hostStr, wx.DefaultPosition)
        self.pathCtrl = wx.TextCtrl(pathPanel, -1, pathStr, wx.DefaultPosition)
        self.hostLabel = wx.StaticText(hostPanel, -1, 'Remote Host', wx.DefaultPosition)
        self.pathLabel = wx.StaticText(pathPanel, -1, 'Remote Path', wx.DefaultPosition)
        self.save = wx.Button(buttonPanel, -1, "Save")
        self.cancel = wx.Button(buttonPanel, -1, "Cancel")
        hostBox.Add(self.hostLabel, 0, wx.ALL, 5)
        hostBox.Add(self.hostCtrl, 0, wx.ALL, 5)
        pathBox.Add(self.pathLabel, 0, wx.ALL, 5)
        pathBox.Add(self.pathCtrl, 0, wx.ALL, 5)
        buttonBox.Add(self.save, 0, wx.LEFT, 5)
        buttonBox.Add(self.cancel)
        hostPanel.SetSizer(hostBox)
        pathPanel.SetSizer(pathBox)
        buttonPanel.SetSizer(buttonBox)
        mainBox.Add(hostPanel, 0)
        mainBox.Add(pathPanel, 0)
        mainBox.Add(buttonPanel, 0)
        self.SetSizer(mainBox)
        self.save.Bind(wx.EVT_BUTTON, self.saveRemote)
        self.cancel.Bind(wx.EVT_BUTTON, self.closeDialog)

    # noinspection PyUnusedLocal
    def saveRemote(self, evt):
        self._drive[AEnums.REMOTEHOST] = self.hostCtrl.GetValue()
        self._drive[AEnums.REMOTEPATH] = self.pathCtrl.GetValue()
        self.r.updateDrive(self._drive)
        self.Close(1)

    # noinspection PyUnusedLocal
    def closeDialog(self, evt):
        self.Close(1)


class SearchDialog(wx.Dialog):
    CONTAINS = 'Contains'
    SQL = 'SQL'
    ENDS = 'Ends With'
    STARTS = 'Starts With'
    CHOICES = [CONTAINS, SQL, ENDS, STARTS]
    TYPECHOICES = [AEnums.DIR, AEnums.FILE, AEnums.ALL]

    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, -1, 'Search', wx.DefaultPosition, wx.Size(250, 350))
        bagSizer = wx.GridBagSizer(hgap=5, vgap=5)
        self.queryChoice = wx.ComboBox(self, -1, self.CONTAINS, choices=self.CHOICES,
                                       style=wx.CB_READONLY | wx.CB_SORT | wx.CB_DROPDOWN)
        self.drives = parent.getDrives()
        self.driveChoices = ['{0}-{1}'.format(x[AEnums.NAME], x[AEnums.SERIAL]) for x in self.drives] + [AEnums.ALL]
        self.drives += [None]
        self.driveChoice = wx.ComboBox(self, -1, AEnums.ALL, choices=self.driveChoices,
                                       style=wx.CB_READONLY | wx.CB_DROPDOWN)
        self.type = wx.RadioBox(self, -1, 'Type', choices=self.TYPECHOICES, style=wx.HORIZONTAL)
        self.type.SetSelection(self.TYPECHOICES.index(AEnums.ALL))
        self.queryBox = wx.TextCtrl(self, -1, '')
        ok = wx.Button(self, wx.ID_OK, 'Ok')
        cancel = wx.Button(self, wx.ID_CANCEL, 'Cancel')
        bagSizer.Add(wx.StaticText(self, -1, 'Query'), pos=(0, 0), border=5, flag=wx.LEFT | wx.TOP)
        bagSizer.Add(self.queryBox, pos=(0, 1), border=5, flag=wx.RIGHT | wx.TOP | wx.EXPAND)
        bagSizer.Add(wx.StaticText(self, -1, 'Query Type'), pos=(1, 0), border=5, flag=wx.LEFT)
        bagSizer.Add(self.queryChoice, pos=(1, 1), border=5, flag=wx.RIGHT | wx.EXPAND)
        bagSizer.Add(wx.StaticText(self, -1, 'Drive'), pos=(2, 0), border=5, flag=wx.LEFT)
        bagSizer.Add(self.driveChoice, pos=(2, 1), border=5, flag=wx.RIGHT | wx.EXPAND)
        bagSizer.Add(self.type, pos=(3, 0), span=(1, 2), border=5, flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
        bagSizer.Add(ok, pos=(4, 0), border=5, flag=wx.LEFT | wx.BOTTOM)
        bagSizer.Add(cancel, pos=(4, 1), border=5, flag=wx.RIGHT | wx.BOTTOM)
        self.SetSizer(bagSizer)
        self.Fit()
        self.Bind(wx.EVT_BUTTON, self.returnQuery, ok)
        self.Bind(wx.EVT_BUTTON, self.cancel, cancel)
        self.queryBox.SetFocus()
        self.value = None

    # noinspection PyUnusedLocal
    def returnQuery(self, evt=None):
        driveType = self.TYPECHOICES[self.type.GetSelection()]
        drive = self.drives[self.driveChoices.index(self.driveChoice.GetValue())]
        queryType = self.queryChoice.GetValue()
        query = self.queryBox.GetValue()
        if queryType == self.CONTAINS:
            queryStr = '%{0}%'.format(query)
        elif queryType == self.SQL:
            queryStr = query
        elif queryType == self.ENDS:
            queryStr = '%{0}'.format(query)
        else:
            queryStr = '{0}%'.format(query)
        self.value = {
            AEnums.DRIVE: drive,
            AEnums.TYPE: driveType,
            AEnums.QUERY: queryStr,
        }
        self.EndModal(wx.ID_OK)

    # noinspection PyUnusedLocal
    def cancel(self, evt=None):
        self.EndModal(wx.ID_CANCEL)

    def GetValue(self):
        return self.value


class manageDirDialog(wx.Dialog):
    def __init__(self, parent, driveRef):
        self._drive = driveRef
        self.r = SQLLite()
        self._dirList = self.r.getPaths(self._drive)
        label = '%s - Directories' % driveRef[AEnums.NAME]
        wx.Dialog.__init__(self, parent, -1, label, wx.DefaultPosition, wx.Size(250, 350))
        self.mgr = aui.AuiManager(self)
        self.dirBox = wx.ListBox(self, -1, style=wx.LB_EXTENDED)
        for directory in self._dirList:
            self.dirBox.Insert(str(directory), 0, None)
        buttonPanel = wx.Panel(self, -1, size=(-1, 32))
        addDir = wx.Button(buttonPanel, -1, 'Add...')
        removeDir = wx.Button(buttonPanel, -1, 'Remove')
        buttonBox = wx.BoxSizer(wx.HORIZONTAL)
        buttonBox.Add(addDir, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.TOP, 5)
        buttonBox.Add(removeDir, 0, wx.BOTTOM | wx.RIGHT | wx.TOP, 5)
        buttonPanel.SetSizer(buttonBox)
        self.mgr.AddPane(self.dirBox,
                         aui.AuiPaneInfo().Name('Directories').Floatable(False).Caption('Directories').Gripper(
                             False).Center().CloseButton(False))
        self.mgr.AddPane(buttonPanel,
                         aui.AuiPaneInfo().Name('Buttons').Gripper(False).CaptionVisible(False).Bottom().CloseButton(
                             False))
        self.mgr.Update()
        self.Bind(wx.EVT_BUTTON, self.removeDir, removeDir)
        self.Bind(wx.EVT_BUTTON, self.addDir, addDir)

    def removeDir(self, evt):
        remove = self.dirBox.GetSelections()
        selectList = self._dirList[:]
        selectList.reverse()
        for index in remove:
            with self.r.batch():
                self.r.removePath(self._drive, selectList[index])
        self.r.commit()
        self._dirList = self.r.getPaths(self._drive)
        self.changeList(evt)

    def owns(self, dirIn):
        letter = DriveUtil.getLetter(self._drive[AEnums.NAME], self._drive[AEnums.SERIAL])
        if re.search(r'^%s' % letter, dirIn, re.IGNORECASE):
            if os.path.exists(dirIn):
                return True
        return False

    def addDir(self, evt):
        dialog = wx.DirDialog(self, message='Pick a directory to index.')
        if dialog.ShowModal() == wx.ID_OK:
            if self.owns(dialog.GetPath()):
                result = self.r.addPath(self._drive, dialog.GetPath().strip(os.sep))
                self._dirList = self.r.getPaths(self._drive)
                if not result:
                    error = wx.MessageDialog(self, 'Error Adding Dir, is it a subdir of an already added directory?',
                                             'Add Error', wx.OK | wx.ICON_ERROR)
                    error.ShowModal()
                elif result == AEnums.OVERWRITE:
                    warn = wx.MessageDialog(self,
                                            'The directory added contained one or more of the subdirectories ' +
                                            'already present, sub dirs removed.',
                                            'Overwrote Subdir(s)', wx.OK | wx.ICON_WARNING)
                    warn.ShowModal()
                    self.changeList(evt)
                else:
                    self.changeList(evt)

    # noinspection PyUnusedLocal
    def changeList(self, event):
        """ populates the listbox with the skip list of the valid extension list depending on whath as been selected """
        self.dirBox.Clear()
        for item in self._dirList:
            self.dirBox.Insert(item, 0, None)


class listdialog(wx.Dialog):
    """ a custom dialog which allows a user to provide regular expressions for file extensions
        to include filenames to exclude.
    Public functions:
    close -- closes the dialog"""

    def __init__(self, parent, buManager):
        self.buObject = buManager
        skipList = buManager.getSkipList()
        label = "Extensions and Skiplist Management"
        wx.Dialog.__init__(self, parent, -1, label, wx.DefaultPosition, wx.Size(250, 350))
        listSelPanel = wx.Panel(self)
        buttonPanel = wx.Panel(self)
        listSelBox = wx.BoxSizer(wx.HORIZONTAL)
        buttonBox = wx.BoxSizer(wx.HORIZONTAL)
        mainBox = wx.BoxSizer(wx.VERTICAL)
        self.ids = {'listLabel': 1, 'listSelect': 2, 'listBox': 3, 'add': 4, 'remove': 5, 'cancel': 6}
        self.listLabel = wx.StaticText(listSelPanel, self.ids['listLabel'], "List")
        self.listSelect = wx.ComboBox(listSelPanel, self.ids['listSelect'], "Skip", wx.DefaultPosition, wx.DefaultSize,
                                      ['Skip', 'Valid Ext'])
        self.listBox = wx.ListBox(self, self.ids['listBox'], style=wx.LB_EXTENDED)
        self.add = wx.Button(buttonPanel, self.ids['add'], "Add")
        self.remove = wx.Button(buttonPanel, self.ids['remove'], "Remove")
        self.cancel = wx.Button(buttonPanel, self.ids['cancel'], "Close")
        listSelBox.Add(self.listLabel, 0, wx.LEFT | wx.TOP | wx.RIGHT, 8)
        listSelBox.Add(self.listSelect, 0, wx.TOP | wx.RIGHT, 5)
        listSelPanel.SetSizer(listSelBox)
        buttonBox.Add(self.add, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM, 5)
        buttonBox.Add(self.remove, 0, wx.BOTTOM | wx.RIGHT, 5)
        buttonBox.Add(self.cancel, 0, wx.BOTTOM | wx.RIGHT, 5)
        buttonPanel.SetSizer(buttonBox)
        mainBox.Add(listSelPanel, 0)
        mainBox.Add(self.listBox, 1, wx.ALL | wx.EXPAND, 5)
        mainBox.Add(buttonPanel, 0)
        self.SetSizer(mainBox)
        wx.EVT_BUTTON(self, self.ids['add'], self.addStr)
        wx.EVT_BUTTON(self, self.ids['remove'], self.removeStr)
        wx.EVT_BUTTON(self, self.ids['cancel'], self.close)
        wx.EVT_COMBOBOX(self, self.ids['listSelect'], self.changeList)
        for skip in skipList:
            self.listBox.Insert(skip, 0, None)

    # noinspection PyUnusedLocal
    def close(self, event):
        """ closes the dialog """
        self.Close(1)

    # noinspection PyBroadException
    def addStr(self, event):
        """ promps for a regular expression and adds it to the skip list or the valid extension list """
        dialog = wx.TextEntryDialog(self, "Enter new " + self.listSelect.GetValue(),
                                    "Add " + self.listSelect.GetValue(), "", style=wx.OK | wx.CANCEL)
        if dialog.ShowModal() == wx.ID_CANCEL:
            return
        else:
            add = dialog.GetValue()
            try:
                # A naive regular expression validator -- the assumption being that if the regex is invalid an
                # exception will be thrown
                re.search(add, 'test')
            except:
                wx.MessageDialog(self, 'The regular expression you entered is invalid.', 'Invalid Regex!',
                                 wx.OK | wx.ICON_ERROR).ShowModal()
            else:
                if re.search("skip", self.listSelect.GetValue(), re.IGNORECASE):
                    self.buObject.addSkip(add)
                else:
                    self.buObject.addValidExt(add)
                self.changeList(event)

    def getList(self):
        """ returns the current skip list or valid extension list """
        if re.search("skip", self.listSelect.GetValue(), re.IGNORECASE):
            currList = self.buObject.getSkipList()
        else:
            currList = self.buObject.getValidExt()
        return currList

    def removeStr(self, event):
        """ removes a given string from the skip lits or the valid extension list """
        remove = self.listBox.GetSelections()
        currList = self.getList()
        for index in sorted(remove, reverse=True):
            del currList[index]
        if re.search("skip", self.listSelect.GetValue(), re.IGNORECASE):
            self.buObject.setSkipList(currList)
        else:
            self.buObject.setValidExt(currList)
        self.changeList(event)

    # noinspection PyUnusedLocal
    def changeList(self, event):
        """ populates the listbox with the skip list of the valid extension list depending on whath as been selected """
        currList = self.getList()
        self.listBox.Clear()
        for item in currList:
            self.listBox.Insert(item, 0, None)
